using System;
using System.Collections;
using System.Threading;
#if !PocketPC || DesignTime
using System.ComponentModel;
#endif
using System.Reflection;

namespace GeoFramework
{
    /// <summary>
    /// Represents a collection of interpolated values using realistic acceleration and deceleration.
    /// </summary>
    /// <remarks>This enumeration is used by several GeoFramework controls to smoothly transition from
    /// one value to another.  For example, the GPS SatelliteViewer uses acceleration to smoothly
    /// transition from one bearing to another, giving the appearance of a realistic compass.  This
    /// enumeration, when combined with the <see cref="Interpolator"></see> class lets you add smooth
    /// transitions to your own controls as well.</remarks>
    public enum InterpolationMethod
    {
        /// <summary>
        /// The transition occurs at a steady rate.
        /// </summary>
        Linear = 0,
        /// <summary>
        /// The transition is immediate; no interpolation takes place.
        /// </summary>
        Snap,
        /// <summary>
        /// The transition starts at zero and accelerates to the end using a quadratic formula.
        /// </summary>
        QuadraticEaseIn,
        /// <summary>
        /// The transition starts at high speed and decelerates to zero.
        /// </summary>
        QuadraticEaseOut,
        /// <summary>
        /// The transition accelerates to the halfway point, then decelerates to zero.
        /// </summary>
        QuadraticEaseInAndOut,
        CubicEaseIn,
        CubicEaseOut,
        CubicEaseInOut,
        QuarticEaseIn,
        ExponentialEaseIn,
        ExponentialEaseOut
    }

	/// <summary>Calculates intermediate values between two bounding values.</summary>
	/// <remarks>
	/// This powerful class provides the ability to interpolate values based on varying
	/// interpolation techniques. This class is used primarily to simulate realistic motion by
	/// accelerating and decelerating. This class is also used to calculate intermediate values
	/// for features such as image georeferencing and estimating precision errors.
	/// </remarks>
#if !PocketPC || DesignTime
#if Framework20
    [Obfuscation(Feature = "renaming", Exclude = false, ApplyToMembers = true)]
    [Obfuscation(Feature = "controlflow", Exclude = true, ApplyToMembers = true)]
    [Obfuscation(Feature = "stringencryption", Exclude = false, ApplyToMembers = true)]
#endif
    [TypeConverter(typeof(ExpandableObjectConverter))]
    [Serializable()]
#endif
    public sealed class Interpolator
    {
        private int pCount;
        private double pMinimum;
        private double pMaximum;
        private InterpolationMethod pInterpolationMethod;
        private double[] pValues;
        private object SyncRoot = new object();

        public Interpolator()
        {
			lock(SyncRoot)
			{
				pCount = 10;
				pInterpolationMethod = InterpolationMethod.Linear;
				Recalculate();
			}
        }

        public Interpolator(int count, InterpolationMethod mode)
        {
			lock(SyncRoot)
			{
				pCount = count;
				pInterpolationMethod = mode;
				Recalculate();
			}
        }

        public Interpolator(double minimum, double maximum, int count)
        {
			lock(SyncRoot)
			{
				pMinimum = minimum;
				pMaximum = maximum;
				pCount = count;
				Recalculate();
			}
        }

        public Interpolator(double minimum, double maximum, int count, InterpolationMethod mode)
        {
			lock(SyncRoot)
			{
				pMinimum = minimum;
				pMaximum = maximum;
				pCount = count;
				pInterpolationMethod = mode;
				Recalculate();
			}
        }

		/// <summary>Controls the smallest number in the sequence.</summary>
        public double Minimum
        {
            get
            {
                return pMinimum;
            }
            set
            {
				lock(SyncRoot)
				{
					if (pMinimum == value)
						return;
					pMinimum = value;
					Recalculate();
				}
            }
        }

		/// <summary>Controls the largest number in the sequence.</summary>
        public double Maximum
        {
            get
            {
                return pMaximum;
            }
            set
            {
				lock(SyncRoot)
				{
					if (pMaximum == value) return;
					pMaximum = value;
					Recalculate();
				}
            }
        }

		/// <summary>Controls the acceleration and/or deceleration technique used.</summary>
        public InterpolationMethod InterpolationMethod
        {
            get
            {
                return pInterpolationMethod;
            }
            set
            {
				lock(SyncRoot)
				{
					if (pInterpolationMethod == value) return;
					pInterpolationMethod = value;
					Recalculate();
				}
            }
        }

		/// <summary>Controls the number of interpolated values.</summary>
        public int Count
        {
            get
            {
                return pCount;
            }
            set
            {
				lock(SyncRoot)
				{
					if (pCount == value) 
						return;
					pCount = value;
					// Recalculate the array
					Recalculate();
				}
            }
        }

		/// <summary>Reverses the interpolated sequence.</summary>
        public void Swap()
        {
			lock(SyncRoot)
			{
				double Temp = pMinimum;
				pMinimum = pMaximum;
				pMaximum = Temp;
				Recalculate();
			}
        }

		/// <summary>Returns a number from the interpolated sequence.</summary>
        public double this[int index]
        {
            get
            {				
				//lock(SyncRoot)
				{
					if (index > Count - 1)
						return pValues[Count - 1];
					else if (index < 0)
						return pValues[0];
					else
						return pValues[index];
				}
            }
        }

        // Recalculates all values according to the specified mode
		private void Recalculate()
		{
			// Recalculate the entire array
			pValues = new double[pCount];
			for (int Iteration = 0; Iteration < pCount; Iteration++)
			{
				pValues[Iteration] = CalculateValue(Iteration);
			}
		}

        private double CalculateValue(double Index) // int	getFormula(string animType, float Index, float b, float d, float c)
        {
            //adjust formula to selected algoritm from combobox
            switch (this.InterpolationMethod)
            {
                case InterpolationMethod.Snap:
                    return pMaximum;
                case InterpolationMethod.Linear:
                    // Simple linear values - no acceleration or deceleration 
                    return ((pMaximum - pMinimum) * Index / Count + pMinimum);
                case InterpolationMethod.QuadraticEaseIn:
                    // Quadratic (Time ^ 2) easing in - accelerating from zero velocity
                    return ((pMaximum - pMinimum) * (Index /= Count) * Index + pMinimum);
                case InterpolationMethod.QuadraticEaseOut:
                    // Quadratic (Index^2) easing out - decelerating to zero velocity
                    return (-(pMaximum - pMinimum) * (Index = Index / Count) * (Index - 2) + pMinimum);
                case InterpolationMethod.QuadraticEaseInAndOut:
                    // Quadratic easing in/out - acceleration until halfway, then deceleration
                    if ((Index /= Count * 0.5) < 1)
                    {
                        return ((pMaximum - pMinimum) * 0.5 * Index * Index + pMinimum);
                    }
                    else
                    {
                        return (-(pMaximum - pMinimum) * 0.5 * ((--Index) * (Index - 2) - 1) + pMinimum);
                    }
                case InterpolationMethod.CubicEaseIn:
                    // Cubic easing in - accelerating from zero velocity
                    return ((pMaximum - pMinimum) * (Index /= Count) * Index * Index + pMinimum);
                case InterpolationMethod.CubicEaseOut:
                    // Cubic easing in - accelerating from zero velocity
                    return ((pMaximum - pMinimum) * ((Index = Index / Count - 1) * Index * Index + 1) + pMinimum);
                case InterpolationMethod.CubicEaseInOut:
                    // Cubic easing in - accelerating from zero velocity
                    if ((Index /= Count * 0.5) < 1)
                    {
                        return ((pMaximum - pMinimum) * 0.5 * Index * Index * Index + pMinimum);
                    }
                    else
                    {
                        return ((pMaximum - pMinimum) * 0.5 * ((Index -= 2) * Index * Index + 2) + pMinimum);
                    }
                case InterpolationMethod.QuarticEaseIn:
                    // Quartic easing in - accelerating from zero velocity
                    return ((pMaximum - pMinimum) * (Index /= Count) * Index * Index * Index + pMinimum);
                case InterpolationMethod.ExponentialEaseIn:
                    // Exponential (2^Index) easing in - accelerating from zero velocity
                    if (Index == 0)
                    {
                        return pMinimum;
                    }
                    else
                    {
                        return ((pMaximum - pMinimum) * Math.Pow(2, (10 * (Index / Count - 1))) + pMinimum);
                    }
                case InterpolationMethod.ExponentialEaseOut:
                    // exponential (2^Index) easing out - decelerating to zero velocity
                    if (Index == Count)
                    {
                        return (pMinimum + (pMaximum - pMinimum));
                    }
                    else
                    {
                        return ((pMaximum - pMinimum) * (-Math.Pow(2, -10 * Index / Count) + 1) + pMinimum);
                    }
                default:
                    return 0;
            }
        }
    }
}
